package com.Lemon.Zhkuhealth.provider.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.Lemon.Zhkuhealth.common.pojo.entity.CheckGroup;
import com.Lemon.Zhkuhealth.common.pojo.entity.CheckGroupCheckItemXref;
import com.Lemon.Zhkuhealth.common.pojo.entity.CheckItem;
import com.Lemon.Zhkuhealth.common.pojo.entity.Setmeal;
import com.Lemon.Zhkuhealth.common.pojo.entity.SetmealCheckGroupXref;
import com.Lemon.Zhkuhealth.common.util.PageResult;
import com.Lemon.Zhkuhealh.api.service.ISetmealService;
import com.Lemon.Zhkuhealth.provider.mapper.CheckGroupCheckItemXrefMapper;
import com.Lemon.Zhkuhealth.provider.mapper.CheckGroupMapper;
import com.Lemon.Zhkuhealth.provider.mapper.CheckItemMapper;
import com.Lemon.Zhkuhealth.provider.mapper.SetmealCheckGroupXrefMapper;
import com.Lemon.Zhkuhealth.provider.mapper.SetmealMapper;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Some description here.
 *
 * @author LanPingX.com
 * @version 1.0, 2023/08/25
 */
@DubboService
@RequiredArgsConstructor
@Slf4j
public class SetmealServiceImpl implements ISetmealService {

    private final SetmealMapper setmealMapper;
    private final SetmealCheckGroupXrefMapper setmealCheckGroupXrefMapper;
    private final CheckGroupMapper checkGroupMapper;
    private final CheckGroupCheckItemXrefMapper checkGroupCheckItemXrefMapper;
    private final CheckItemMapper checkItemMapper;

    private final RedisTemplate<String, String> redisTemplate;

    private final FreeMarkerConfigurer freeMarkerConfigurer;
    @Value("${out-put-path}")
    private String outPutPath;

    @Override
    public void save(Setmeal setmeal, List<Long> checkgroupIds) {
        // 保存套餐信息
        setmealMapper.insert(setmeal);
        // 保存套餐与检查组关系
        for (Long checkgroupId : checkgroupIds) {
            SetmealCheckGroupXref setmealCheckGroupXref = new SetmealCheckGroupXref();
            setmealCheckGroupXref.setSetmealId(setmeal.getId());
            setmealCheckGroupXref.setCheckGroupId(checkgroupId);
            setmealCheckGroupXrefMapper.insert(setmealCheckGroupXref);
        }
        // 1. 把最终要保存的图片名称保存到Redis
//       // redisTemplate.opsForSet().add(RedisConstant.SETMEAL_IMG_RESOURCES_DB, setmeal.getImg());
        // 移动到任务调度服务中执行
//       // 2. 获取两个Set之间的差值，就是找出所有在七牛云中中但是不在数据库中的图片名称
//       Set<String> differenceSet
//                = redisTemplate.opsForSet().difference(RedisConstant.SETMEAL_IMG_RESOURCES, RedisConstant.SETMEAL_IMG_RESOURCES_DB);
//        // 3. 检查差值集合是否为空
//        if (differenceSet != null) {
//            differenceSet.forEach(fileName -> {
//                // 4. 把临时上传的图片名称进行删除
//                QiniuUtils.delete(fileName);
//                // 5. 把Redis中的图片名称也删除
//                redisTemplate.opsForSet().remove(RedisConstant.SETMEAL_IMG_RESOURCES, fileName);
//            });
//        }

    }

    /**
     * 生成静态页面
     */
    public void generateMobileStaticHtml() {
        //准备模板文件中所需的数据
        List<Setmeal> setmealList = this.list();
        //生成套餐列表静态页面
        generateMobileSetmealListHtml(setmealList);
        //生成套餐详情静态页面（多个）
        generateMobileSetmealDetailHtml(setmealList);
    }

    /**
     * 生成套餐列表静态页面
     * @param setmealList
     */
    public void generateMobileSetmealListHtml(List<Setmeal> setmealList) {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("setmealList", setmealList);
        String outputFileName = "setmeal_ftl.html";  // 使用一个变量来保存文件名，以增强代码的可读性（可选）
        this.generateHtml("mobile_setmeal.ftl", outputFileName, dataModel);
    }


    /**
     * 生成套餐详情静态页面（多个）
     * @param setmealList
     */
    public void generateMobileSetmealDetailHtml(List<Setmeal> setmealList) {
        for (Setmeal setmeal : setmealList) {
            Map<String, Object> dataModel = new HashMap<>();
            Setmeal detailedSetmeal = this.getById(setmeal.getId());
            dataModel.put("setmeal", detailedSetmeal);
            String outputFileName = "setmeal_detail_" + setmeal.getId().toString() + ".html";
            this.generateHtml("mobile_setmeal_detail.ftl", outputFileName, dataModel);
        }
    }


    public void generateHtml(String templateName, String htmlPageName, Map<String, Object> dataModel) {
        Configuration configuration = freeMarkerConfigurer.getConfiguration();
        File file = new File(outPutPath + File.separator + htmlPageName);
        try (Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)))) {
            // 加载模板文件
            Template template = configuration.getTemplate(templateName);
            // 输出文件
            template.process(dataModel, out);
        } catch (IOException | TemplateException e) {
            log.error("输出静态套餐页面出现问题", e);  // 注意这里我们传入整个异常对象，而不只是消息
        }
    }


    @Override
    public List<Setmeal> list() {
        return setmealMapper.selectList(null);
    }

    @Override
    public PageResult<Setmeal> page(Integer currentPage, Integer pageSize, String queryString) {
        LambdaQueryWrapper<Setmeal> qw = new LambdaQueryWrapper<>();
        qw.like(null != queryString, Setmeal::getName, queryString)
                .or()
                .like(null != queryString, Setmeal::getCode, queryString)
                .or()
                .like(null != queryString, Setmeal::getHelpCode, queryString);
        Page<Setmeal> page = new Page<>(currentPage, pageSize);
        setmealMapper.selectPage(page, qw);
        return new PageResult<>(page.getTotal(), page.getRecords());
    }

    @Override
    public Setmeal getById(Long id) {
        Setmeal setmeal = setmealMapper.selectById(id);
        QueryWrapper<SetmealCheckGroupXref> setmealCheckGroupXrefMapperQw = new QueryWrapper<>();
        setmealCheckGroupXrefMapperQw.eq("setmeal_id", id);
        List<SetmealCheckGroupXref> setmealCheckGroupXrefs
                = setmealCheckGroupXrefMapper.selectList(setmealCheckGroupXrefMapperQw);
        List<Long> checkGroupIdList
                = setmealCheckGroupXrefs.stream().map(SetmealCheckGroupXref::getCheckGroupId).toList();
        ArrayList<CheckGroup> checkGroups = new ArrayList<>();
        for (Long checkGroupId : checkGroupIdList) {
            CheckGroup checkGroup = checkGroupMapper.selectById(checkGroupId);
            ArrayList<CheckItem> checkItems = new ArrayList<>();
            QueryWrapper<CheckGroupCheckItemXref> checkGroupCheckItemXrefQw = new QueryWrapper<>();
            checkGroupCheckItemXrefQw.eq("checkgroup_id", checkGroupId);
            List<CheckGroupCheckItemXref> checkGroupCheckItemXrefs
                    = checkGroupCheckItemXrefMapper.selectList(checkGroupCheckItemXrefQw);
            List<Long> checkItemIdlist
                    = checkGroupCheckItemXrefs.stream().map(CheckGroupCheckItemXref::getCheckItemId).toList();
            for (Long checkItemId : checkItemIdlist) {
                CheckItem checkItem = checkItemMapper.selectById(checkItemId);
                checkItems.add(checkItem);
            }
            checkGroup.setCheckItems(checkItems);
            checkGroups.add(checkGroup);
        }
        setmeal.setCheckGroups(checkGroups);
        return setmeal;
    }
    @Override
    public Setmeal findById(Long id) {
        // 1. 首先查询套餐基本信息
        Setmeal setmeal = setmealMapper.selectById(id);

        // *** 关键修改1：如果套餐不存在，直接返回 null，避免后续操作的 NullPointerException ***
        if (setmeal == null) {
            log.warn("查询套餐详情失败，ID为 {} 的套餐不存在。", id);
            return null; // 或者抛出自定义异常，表示资源未找到
        }

        // 2. 查询套餐关联的检查组ID列表
        QueryWrapper<SetmealCheckGroupXref> setmealCheckGroupXrefMapperQw = new QueryWrapper<>();
        setmealCheckGroupXrefMapperQw.eq("setmeal_id", id);
        List<SetmealCheckGroupXref> setmealCheckGroupXrefs =
                setmealCheckGroupXrefMapper.selectList(setmealCheckGroupXrefMapperQw);

        // *** 关键修改2：对 setmealCheckGroupXrefs 进行 null 检查和非空判断 ***
        // 确保 setmealCheckGroupXrefs 不是 null，并且有数据，再进行 stream 操作
        List<Long> checkGroupIdList = new ArrayList<>(); // 初始化为空列表
        if (setmealCheckGroupXrefs != null && !setmealCheckGroupXrefs.isEmpty()) {
            // 这行是你的原始代码中的大约第218行
            checkGroupIdList = setmealCheckGroupXrefs.stream()
                    .map(SetmealCheckGroupXref::getCheckGroupId)
                    .collect(Collectors.toList()); // 使用 collect 代替 toList() 以兼容不同Java版本
        }

        // 3. 根据检查组ID列表查询检查组详情，并关联检查项
        ArrayList<CheckGroup> checkGroups = new ArrayList<>();
        if (!checkGroupIdList.isEmpty()) { // 只有当有检查组ID时才进行循环
            for (Long checkGroupId : checkGroupIdList) {
                CheckGroup checkGroup = checkGroupMapper.selectById(checkGroupId);
                // *** 关键修改3：检查查询到的 checkGroup 是否为 null ***
                if (checkGroup != null) {
                    ArrayList<CheckItem> checkItems = new ArrayList<>();
                    QueryWrapper<CheckGroupCheckItemXref> checkGroupCheckItemXrefQw = new QueryWrapper<>();
                    checkGroupCheckItemXrefQw.eq("checkgroup_id", checkGroupId);
                    List<CheckGroupCheckItemXref> checkGroupCheckItemXrefs =
                            checkGroupCheckItemXrefMapper.selectList(checkGroupCheckItemXrefQw);

                    // *** 关键修改4：检查 checkGroupCheckItemXrefs 是否为 null/空 ***
                    List<Long> checkItemIdlist = new ArrayList<>(); // 初始化为空列表
                    if (checkGroupCheckItemXrefs != null && !checkGroupCheckItemXrefs.isEmpty()) {
                        checkItemIdlist = checkGroupCheckItemXrefs.stream()
                                .map(CheckGroupCheckItemXref::getCheckItemId)
                                .collect(Collectors.toList());
                    }

                    if (!checkItemIdlist.isEmpty()) { // 只有当有检查项ID时才进行循环
                        for (Long checkItemId : checkItemIdlist) {
                            CheckItem checkItem = checkItemMapper.selectById(checkItemId);
                            // *** 关键修改5：检查查询到的 checkItem 是否为 null ***
                            if (checkItem != null) {
                                checkItems.add(checkItem);
                            } else {
                                log.warn("根据ID {} 查询检查项失败，可能已被删除。", checkItemId);
                            }
                        }
                    }
                    checkGroup.setCheckItems(checkItems);
                    checkGroups.add(checkGroup);
                } else {
                    log.warn("根据ID {} 查询检查组失败，可能已被删除或不存在。", checkGroupId);
                }
            }
        }
        setmeal.setCheckGroups(checkGroups);
        return setmeal;
    }
    /**
     * 根据套餐ID查询关联的检查组ID列表
     * @param id 套餐ID
     * @return 关联的检查组ID列表
     */
    @Override
    public List<Long> findCheckGroupIdsBySetmealId(Long id) {
        QueryWrapper<SetmealCheckGroupXref> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("checkgroup_id").eq("setmeal_id", id);
        List<SetmealCheckGroupXref> xrefs = setmealCheckGroupXrefMapper.selectList(queryWrapper);
        return xrefs.stream()
                .map(SetmealCheckGroupXref::getCheckGroupId)
                .toList();
    }

    /**
     * 编辑套餐，并更新关联检查组
     * @param setmeal 套餐基本信息
     * @param checkGroupIds 关联的检查组ID列表
     */
    @Override
    @Transactional // 开启事务管理
    public void edit(Setmeal setmeal, List<Long> checkGroupIds) {
        // 1. 更新套餐基本信息
        setmealMapper.updateById(setmeal);

        // 2. 清理原有套餐和检查组的关联关系
        QueryWrapper<SetmealCheckGroupXref> deleteWrapper = new QueryWrapper<>();
        deleteWrapper.eq("setmeal_id", setmeal.getId());
        setmealCheckGroupXrefMapper.delete(deleteWrapper);

        // 3. 重新建立新的关联关系
        if (checkGroupIds != null && !checkGroupIds.isEmpty()) {
            for (Long checkGroupId : checkGroupIds) {
                SetmealCheckGroupXref xref = new SetmealCheckGroupXref();
                xref.setSetmealId(setmeal.getId());
                xref.setCheckGroupId(checkGroupId);
                setmealCheckGroupXrefMapper.insert(xref);
            }
        }
    }

    /**
     * 根据ID删除套餐
     * @param id 套餐ID
     */
    @Override
    @Transactional // 开启事务管理
    public void deleteById(Long id) {

        // 1. 删除套餐和检查组的关联关系
        QueryWrapper<SetmealCheckGroupXref> deleteXrefWrapper = new QueryWrapper<>();
        deleteXrefWrapper.eq("setmeal_id", id);
        setmealCheckGroupXrefMapper.delete(deleteXrefWrapper);

        // 2. 删除套餐基本信息
        setmealMapper.deleteById(id);
    }

    @Override
    public List<Setmeal> findAll() {
        // 实现查询所有套餐的逻辑
        // 假设 SetmealMapper 继承了 MyBatis-Plus 的 BaseMapper
        log.info("正在查询所有套餐...");
        return setmealMapper.selectList(null); // 调用 Mapper 的 selectList 方法查询所有
    }
}
